package com.angel.resourceBundle.containers.managers;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import com.angel.common.helpers.StringHelper;
import com.angel.resourceBundle.exceptions.InvalidResourceBundleManagerException;
import com.angel.resourceBundle.exceptions.OnFindPropertyValueException;
import com.angel.resourceBundle.exceptions.PropertyValueKeyNotFoundException;
import com.angel.resourceBundle.exceptions.ResourceBundleTypeNotFoundException;
import com.angel.resourceBundle.managers.ResourceBundleManager;
import com.angel.resourceBundle.propertyValue.PropertyValue;
import com.angel.resourceBundle.results.PropertyValueResult;
import com.angel.resourceBundle.results.impl.PropertyValueResultImpl;

public class ResourceBundle implements Serializable{

	private static final long serialVersionUID = 8917008507011808422L;

	private Collection<ResourceBundleManager> resourceBundleManagers;

	public ResourceBundle(){
		super();
		this.resourceBundleManagers = new ArrayList<ResourceBundleManager>();
	}

	public ResourceBundle(Collection<ResourceBundleManager> resourceBundleManagers){
		super();
		this.resourceBundleManagers = resourceBundleManagers;
	}

	public ResourceBundle(ResourceBundleManager... resourceBundleManagers){
		super();
		this.resourceBundleManagers = new ArrayList<ResourceBundleManager>();
		this.transformResourceBundleManager(resourceBundleManagers);
	}

	public boolean containsResourceBundleManager(ResourceBundleManager resourceBundleManager){
		boolean contains = false;
		if(this.resourceBundleManagers.contains(resourceBundleManager)){
			contains = true;
		} else {
			for(ResourceBundleManager m: this.resourceBundleManagers){
				if(m.getResourBundleContextType().equalsIgnoreCase(resourceBundleManager.getResourBundleContextType())){
					contains = true;
					break;
				}
			}
		}
		return contains;
	}

	public void removeResourceBundleManager(ResourceBundleManager resourceBundleManager){
		this.resourceBundleManagers.remove(resourceBundleManager);
	}

	public void addResourceBundleManager(ResourceBundleManager resourceBundleManager){
		if(!this.containsResourceBundleManager(resourceBundleManager)){
			this.resourceBundleManagers.add(resourceBundleManager);
		} else {
			throw new InvalidResourceBundleManagerException("Resource bundle manager couldn't add. It already there is a resource bundle manager with type / name ["+ resourceBundleManager.getResourBundleContextType() + "].");
		}
	}

	private void transformResourceBundleManager(ResourceBundleManager... resourceBundleManagers) {
		if(resourceBundleManagers  != null && resourceBundleManagers.length > 0){
			for(ResourceBundleManager manager : resourceBundleManagers){
				this.addResourceBundleManager(manager);
			}
		}
	}

	/** Find a unique property value with a property key associated with a language.
	 *
	 * @param propertyValueKey to find its property value.
	 * @param languageName associated to property value.
	 * @return a property value.
	 */
	public String findUniquePropertyValueWithLanguageName(String propertyValueKey, String languageName){
		PropertyValueResult propertyValueResult = this.findPropertyValueResultWithLanguageName(languageName, propertyValueKey);
		Long resultSize = propertyValueResult.resultSize();
		if( resultSize > 1){
			throw new OnFindPropertyValueException("Property value not unique [" + resultSize + "] for [" + propertyValueKey + "].");
		}
		return propertyValueResult.getTextFor(propertyValueKey, languageName);
	}

	/** Find a unique property value associated to a key and current resource bundle language.
	 *
	 * @param propertyValueKey to find associated to a key and current language.
	 * @return if it has result it returns a property value, otherwise it returns null.
	 */
	public String findUniquePropertyValue(String propertyValueKey){
		PropertyValueResult propertyValueResult = this.findAllPropertiesValues(propertyValueKey);
		Long resultSize = propertyValueResult.resultSize();
		if( resultSize > 1){
			throw new OnFindPropertyValueException("Property value not unique [" + resultSize + "] for [" + propertyValueKey + "].");
		}
		Iterator<String> iteratorValues = propertyValueResult.getTextsFor(propertyValueKey).iterator();
		return iteratorValues.hasNext() ? iteratorValues.next(): null;
	}



	public String forceFindUniquePropertyValueWithLanguageName(String propertyValueKey, String languageName){
		String propertyValue = this.findUniquePropertyValueWithLanguageName(propertyValueKey, languageName);
		if( propertyValue == null){
			throw new PropertyValueKeyNotFoundException("Property value key [" + propertyValueKey + "] not found.");
		}
		return propertyValue;
	}
	/*
	public String forceFindUniquePropertyValueWithLanguageName(String languageName, String propertyValueKey){
		String propertyValue = this.findUniquePropertyValueWithInternationalizerName(languageName, propertyValueKey);
		if( propertyValue == null){
			throw new PropertyValueKeyNotFoundException("Property value key [" + propertyValueKey + "] not found.");
		}
		return propertyValue;
	}*/
	/** Find all properties values for a property key and associated with resource bundle context current language.
	 *
	 * @param propertiesValuesKeys array to find its properties values associated with current language.
	 * @return a property value result.
	 */
	public PropertyValueResult findAllPropertiesValues(String ... propertiesValuesKeys){
		PropertyValueResult propertyValueResult = null;
		if(propertiesValuesKeys != null && propertiesValuesKeys.length > 0){
			propertyValueResult = this.findPropertyValueResult(propertiesValuesKeys);
		} else {
			propertyValueResult = new PropertyValueResultImpl(new ArrayList<PropertyValue>(), StringHelper.EMPTY_STRING, StringHelper.EMPTY_STRING);
		}
		return propertyValueResult;
	}

	public PropertyValueResult findAllPropertiesValuesWithLanguageName(String languageName){
		PropertyValueResult propertyValueResult = this.findPropertyValueResultWithLanguageName(languageName);
		return propertyValueResult;
	}

	public PropertyValueResult findAllPropertiesValuesWithLanguageName(String languageName, String ... propertiesValuesKeys){
		PropertyValueResult propertyValueResult = null;
		if(propertiesValuesKeys != null && propertiesValuesKeys.length > 0){
			propertyValueResult = this.findPropertyValueResultWithLanguageName(languageName, propertiesValuesKeys);
		} else {
			propertyValueResult = this.findPropertyValueResultWithLanguageName(languageName);
		}
		return propertyValueResult;
	}

	public PropertyValueResult forceFindAllPropertiesValues(String ... propertiesValuesKeys){
		Long resultSizeExpected = this.longArraySize(propertiesValuesKeys);
		PropertyValueResult propertyValueResult = this.findAllPropertiesValues(propertiesValuesKeys);
		return this.checkPropertyValueResult(propertyValueResult, resultSizeExpected);
	}

	public PropertyValueResult forceFindAllPropertiesValuesWithLanguageName(String languageName, String ... propertiesValuesKeys){
		Long resultSizeExpected = this.longArraySize(propertiesValuesKeys);
		PropertyValueResult propertyValueResult = this.findAllPropertiesValuesWithLanguageName(languageName, propertiesValuesKeys);
		return this.checkPropertyValueResult(propertyValueResult, resultSizeExpected);
	}

	public PropertyValueResult findAllPropertiesValuesByArray(String[] propertiesValuesKeys){
		return this.findPropertyValueResult(propertiesValuesKeys);
	}

	public PropertyValueResult findAllPropertiesValuesWithInternationalizerNameByArray(String internationalizerName, String[] propertiesValuesKeys){
		return this.findPropertyValueResultWithLanguageName(internationalizerName, propertiesValuesKeys);
	}

	public PropertyValueResult forceFindAllPropertiesValuesByArray(String[] propertiesValuesKeys){
		Long resultSizeExpected = this.longArraySize(propertiesValuesKeys);
		PropertyValueResult propertyValueResult = this.findAllPropertiesValuesByArray(propertiesValuesKeys);
		return this.checkPropertyValueResult(propertyValueResult, resultSizeExpected);
	}

	public PropertyValueResult forceFindAllPropertiesValuesWithLanguageNameByArray(String languageName, String[] propertiesValuesKeys){
		Long resultSizeExpected = this.longArraySize(propertiesValuesKeys);
		PropertyValueResult propertyValueResult = this.findAllPropertiesValuesWithLanguageName(languageName, propertiesValuesKeys);
		return this.checkPropertyValueResult(propertyValueResult, resultSizeExpected);
	}

	private Long longArraySize(String[] propertiesValuesKeys){
		return Long.valueOf(String.valueOf((propertiesValuesKeys != null? propertiesValuesKeys.length : 0)));
	}

	private PropertyValueResult checkPropertyValueResult(PropertyValueResult propertyValueResult, Long resultSizeExpected){
		if(!propertyValueResult.hasResultSize(resultSizeExpected)){
			throw new PropertyValueKeyNotFoundException("Properties values size [ Expected: " + resultSizeExpected + " - Found: " + propertyValueResult.resultSize() + "].");
		}
		return propertyValueResult;
	}

	public PropertyValueResult findAllPropertiesValues(Collection<String> propertiesValuesKeys){
		String[] propertiesKeys = (String[]) new Object[]{propertiesValuesKeys.toArray()};
		return this.findPropertyValueResult(propertiesKeys);
	}

	public PropertyValueResult findAllPropertiesValuesWithInternationalizerName(String internationalizerName, Collection<String> propertiesValuesKeys){
		String[] propertiesKeys = (String[]) new Object[]{propertiesValuesKeys.toArray()};
		return this.findPropertyValueResultWithLanguageName(internationalizerName, propertiesKeys);
	}

	public PropertyValueResult forceFindAllPropertiesValues(Collection<String> propertiesValuesKeys){
		String[] propertiesKeys = (String[]) new Object[]{propertiesValuesKeys.toArray()};
		Long resultSizeExpected = this.longArraySize(propertiesKeys);
		PropertyValueResult propertyValueResult = this.findAllPropertiesValues(propertiesValuesKeys);
		return this.checkPropertyValueResult(propertyValueResult, resultSizeExpected);
	}

	public PropertyValueResult forceFindAllPropertiesValuesWithInternationalizerName(String internationalizerName, Collection<String> propertiesValuesKeys){
		String[] propertiesKeys = (String[]) new Object[]{propertiesValuesKeys.toArray()};
		Long resultSizeExpected = this.longArraySize(propertiesKeys);
		PropertyValueResult propertyValueResult = this.findAllPropertiesValuesWithInternationalizerName(internationalizerName, propertiesValuesKeys);
		return this.checkPropertyValueResult(propertyValueResult, resultSizeExpected);
	}

	private PropertyValueResult findPropertyValueResultWithLanguageName(String languageName, String ... propertiesValuesKeys){
		PropertyValueResult propertyValueResult = null;
		for(ResourceBundleManager m: this.resourceBundleManagers){
			Collection<PropertyValue> propertiesValues = m.findAllPropertiesValuesForLanguageName(languageName, propertiesValuesKeys);
			String resourceBundleContextKey = m.getResourceBundleContextKey(languageName);
			String separator = m.getSeparator();
			if(propertyValueResult != null){
				propertyValueResult.addKeyValueResult(
						new PropertyValueResultImpl(
							propertiesValues,
							resourceBundleContextKey,
							separator
						)
				);
			} else {
				propertyValueResult = new PropertyValueResultImpl(
						propertiesValues,
						resourceBundleContextKey,
						separator
				);
			}
		}
		return propertyValueResult;
	}

	private PropertyValueResult findPropertyValueResultWithLanguageName(String languageName){
		PropertyValueResult propertyValueResult = null;
		for(ResourceBundleManager m: this.resourceBundleManagers){
			Collection<PropertyValue> propertiesValues = m.findAllPropertiesValuesForLanguageName(languageName);
			String resourceBundleContextKey = m.getResourceBundleContextKey(languageName);
			String separator = m.getSeparator();
			if(propertyValueResult != null){
				propertyValueResult.addKeyValueResult(
						new PropertyValueResultImpl(
							propertiesValues,
							resourceBundleContextKey,
							separator
						)
				);
			} else {
				propertyValueResult = new PropertyValueResultImpl(
						propertiesValues,
						resourceBundleContextKey,
						separator
				);
			}
		}
		return propertyValueResult;
	}

	private PropertyValueResult findPropertyValueResult(String ... propertiesValuesKeys){
		PropertyValueResult propertyValueResult = null;
		for(ResourceBundleManager m: this.resourceBundleManagers){
			if(propertyValueResult != null){
				propertyValueResult.addKeyValueResult(new PropertyValueResultImpl(
						m.findAllPropertiesValuesFor(propertiesValuesKeys),
						m.getResourceBundleContextKey(),
						m.getSeparator()));
			} else {
				propertyValueResult = new PropertyValueResultImpl(
						m.findAllPropertiesValuesFor(propertiesValuesKeys),
						m.getResourceBundleContextKey(),
						m.getSeparator());
			}
		}
		return propertyValueResult;
	}

	public ResourceBundleManager findResourceBundleManager(String resourceBundleType) {
		ResourceBundleManager resourceBundleManager = null;
		for(ResourceBundleManager m: this.resourceBundleManagers){
			if(m.isResourBundleContextType(resourceBundleType)){
				resourceBundleManager = m;
				break;
			}
		}
		if(resourceBundleManager == null){
			throw new ResourceBundleTypeNotFoundException("Resource bundle manager type [" + resourceBundleType + "] not found.");
		}
		return resourceBundleManager;
	}

}
